package com.asen.commons.utils;

import com.asen.commons.core.base.domain.RootModel;
import com.asen.commons.core.base.domain.GenericEntry;
import com.asen.commons.core.base.vo.DcPage;
import com.asen.commons.core.base.model.JsonObject;
import com.asen.commons.core.meta.entity.EntityObject;
import com.asen.commons.core.meta.entity.FieldObject;
import com.asen.commons.core.meta.loader.EntityObjectLoader;
import com.asen.commons.core.utils.EntityUtils;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.serializer.SerializerFeature;
import com.asen.commons.core.base.result.ApiResult;
import com.github.pagehelper.Page;
import com.github.pagehelper.PageInfo;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.MapUtils;
import org.apache.commons.lang3.ArrayUtils;
import org.checkerframework.checker.units.qual.A;

import java.util.*;

/**
 * 显示属性处理
 *
 * @author Gary
 * @version 1.0
 * @date 2022-07-06
 */
@Slf4j
public class ViewDisplayParser {

    public static Object parse(Object object) {
        try {
            if (object instanceof List) {
                // 转换预警
                object = convertRpt(object);
            }
            //else if (object instanceof WarnDataSource) {
            //    // 转换预警
            //    WarnDataSource dataSource = (WarnDataSource) object;
            //    dataSource.setDataList((List) convert(dataSource.getDataList()));
            //}
            else if (object instanceof ApiResult) {
                ApiResult apiResult = (ApiResult) object;
                Object result = apiResult.getResult();
                if (result instanceof PageInfo) {
                    PageInfo pageInfo = (PageInfo) result;
                    pageInfo.setList((List) convert(pageInfo.getList()));
                } else if (result instanceof Page) {
                    apiResult.setResult(convert(result));
                } else if (result instanceof DcPage) {
                    DcPage dcPage = (DcPage) result;
                    dcPage.setList((List) convert(dcPage.getList()));
                } else if (result instanceof Collection) {
                    apiResult.setResult(convert((Collection) result));
                } else {
                    apiResult.setResult(convert(result));
                }
            } else {
                object = convert(object);
            }
        } catch (Exception e) {
            log.error("数据转换错误！", e);
        }
        return object;
    }

    private static Object convert(Object object) throws Exception {
        if (object == null) {
            return null;
        }

        if (EntityUtils.isSimpleType(object.getClass())) {
            return object;
        }

        Set<Object> dataSet = new HashSet<>();

        if (object instanceof Collection) {
            return toJSONArray((Collection) object, dataSet);
        }

        JsonObject jsonObject = toJSON(object, dataSet);
        return jsonObject;
    }

    private static JsonObject toJSON(Object model, Set<Object> dataSet) throws Exception {
        clearIgnoreProperties(model);

        JSONObject jsonObject = parseJson(model);

        JsonObject json = new JsonObject();
        resolveDisplay(json, model, dataSet);

        putValJson(jsonObject, json);

        return json;
    }

    private static List<Object> toJSONArray(Collection collection, Set<Object> dataSet) throws Exception {
        for (Object o : collection) {
            clearIgnoreProperties(o);
        }

        List<Object> jsonArray = new ArrayList<>();
        resolveDisplay(jsonArray, collection, dataSet);
        return jsonArray;
    }

    private static void clearIgnoreProperties(Object o) {
        if (o instanceof RootModel) {
            RootModel root = (RootModel) o;
            for (String key : getIgnoreExtProperties()) {
                root.remove(key);
            }

            if (o instanceof GenericEntry) {
                ((GenericEntry) o).setParent(null);
            }
        }
    }

    private static String[] getIgnoreProperties() {
        return new String[]{"serialVersionUID"};
    }

    private static String[] getIgnoreExtProperties() {
        return new String[]{"OLD_MODEL"};
    }

    private static void resolveDisplay(List<Object> jsonArray, Collection collection, Set<Object> dataSet) throws Exception {
        if (jsonArray == null || CollectionUtils.isEmpty(collection)) {
            return;
        }

        for (Object o : collection) {
            if (o == null) {
                jsonArray.add(null);
                continue;
            }

            Class<?> valueClazz = o.getClass();
            if (EntityUtils.isSimpleType(valueClazz)) {
                jsonArray.add(o);
                continue;
            }

            if (Collection.class.isAssignableFrom(valueClazz)) {
                List<Object> subArray = new ArrayList<>();
                jsonArray.add(subArray);
                resolveDisplay(subArray, (Collection) o, dataSet);
                continue;
            }

            JSONObject jsonObject = parseJson(o);

            JsonObject json = new JsonObject();
            jsonArray.add(json);
            resolveDisplay(json, o, dataSet);

            putValJson(jsonObject, json);
        }
    }

    private static void resolveDisplay(JsonObject json, Map map, Set<Object> dataSet) throws Exception {
        if (json == null || MapUtils.isEmpty(map)) {
            return;
        }

        Set<Map.Entry> entrySet = map.entrySet();
        for (Map.Entry entry : entrySet) {
            Object key = entry.getKey();
            Object value = entry.getValue();
            if (key == null) {
                continue;
            }

            if (value == null) {
                json.put(key.toString(), null);
                continue;
            }

            Class<?> valueClazz = value.getClass();
            if (EntityUtils.isSimpleType(valueClazz)) {
                json.put(key.toString(), value);
                continue;
            }

            if (Collection.class.isAssignableFrom(valueClazz)) {
                List<Object> jsonArray = new ArrayList<>();
                json.put(key.toString(), jsonArray);
                resolveDisplay(jsonArray, (Collection) value, dataSet);
                continue;
            }

            if (dataSet.contains(value)) {
                json.put(key.toString(), null);
                continue;
            }

            JsonObject subJson = new JsonObject();
            json.put(key.toString(), subJson);
            resolveDisplay(subJson, value, dataSet);
        }
    }

    private static void resolveDisplay(JsonObject json, Object model, Set<Object> dataSet) throws Exception {
        if (json == null || model == null || EntityUtils.isSimpleType(model.getClass())) {
            return;
        }

        if (Map.class.isAssignableFrom(model.getClass())) {
            resolveDisplay(json, (Map) model, dataSet);
            return;
        }

        dataSet.add(model);

        Map<JsonObject, Object> subDataMap = new HashMap<>();
        Map<JsonObject, Map> subMapMap = new HashMap<>();
        EntityObject eo = EntityObjectLoader.getInstance().loadByModel(model);
        for (FieldObject ef : eo.getFieldList()) {
            if (ef.isJsonIgnore()) {
                continue;
            }
            if (ef.isIgnore()) {
                if (json.containsKey(ef.getName())) {
                    continue;
                }
            }

            if (ArrayUtils.contains(getIgnoreProperties(), ef.getName())) {
                continue;
            }

            if (EntityUtils.isSimpleType(ef.getJavaType())) {
                setSimpleValue(json, model, dataSet, eo, ef);
                continue;
            }

            if (Collection.class.isAssignableFrom(ef.getJavaType())) {
                Collection collection = (Collection) ef.getValue(model);
                if (collection == null) {
                    continue;
                }
                List<Object> list = new ArrayList<>();
                json.put(ef.getName(), list);
                resolveDisplay(list, collection, dataSet);
                continue;
            }

            if (ef.isEntry()) {
                Collection collection = (Collection) ef.getValue(model);
                if (collection == null) {
                    continue;
                }
                List<Object> list = new ArrayList<>();
                json.put(ef.getName(), list);
                resolveDisplay(list, collection, dataSet);
                continue;
            }

            Object value = ef.getValueJson(model);
            if (dataSet.contains(value) || value == null) {
                json.put(ef.getName(), null);
                continue;
            }

            JsonObject subJson = new JsonObject();
            json.put(ef.getName(), subJson);
            if (value instanceof Map) {
                subMapMap.put(subJson, (Map) value);
            } else {
                subDataMap.put(subJson, value);
            }
        }

        if (!subDataMap.isEmpty()) {
            Set<Map.Entry<JsonObject, Object>> entrySet = subDataMap.entrySet();
            for (Map.Entry<JsonObject, Object> entry : entrySet) {
                JSONObject jsonObject = parseJson(entry.getValue());

                JsonObject subJson = entry.getKey();
                resolveDisplay(subJson, entry.getValue(), dataSet);

                putValJson(jsonObject, subJson);
            }
        }

        if (!subMapMap.isEmpty()) {
            Set<Map.Entry<JsonObject, Map>> entrySet = subMapMap.entrySet();
            for (Map.Entry<JsonObject, Map> entry : entrySet) {
                JSONObject jsonObject = parseJson(entry.getValue());

                JsonObject subJson = entry.getKey();
                resolveDisplay(subJson, entry.getValue(), dataSet);

                putValJson(jsonObject, subJson);
            }
        }
    }

    private static void setSimpleValue(Map<String, Object> json, Object model, Set<Object> dataSet, EntityObject eo, FieldObject ef) throws Exception {
        try {
            Object value = ef.getValueJson(model);
            if (!json.containsKey(ef.getName()) || json.get(ef.getName()) == null) {
                json.put(ef.getName(), value);
            }

            if (Date.class.isAssignableFrom(ef.getJavaType())) {
                setDateValue(json, value, ef);
                return;
            }

            if (!ef.isTypeOnly()) {
                setExtendValue(json, model, eo, ef);
            }
        } catch (Exception e) {
            log.error("处理VO转换异常！参数属性：{}.{}，消息：{}", model.getClass().getName(), ef.getName(), e.getMessage());
        }
    }

    private static void setExtendValue(Map<String, Object> json, Object model, EntityObject eo, FieldObject ef) throws IllegalAccessException {
        if (ef.isBillType()) {
            setBillTypeDisplay(json, model, ef);
            return;
        }

        //if (ef.isFieldRefUnit()) {
        //    setRefUnitDisplay(json, model, ef, eo);
        //    return;
        //}
        //
        //if (ef.isFieldLCModel()) {
        //    setRefLCModelDisplay(json, model, ef, eo);
        //    return;
        //}

        if (ef.isReference()) {
            setRefDisplay(json, model, ef);
            return;
        }

        if (ef.isEnum()) {
            setEnumDisplay(json, model, ef);
            return;
        }

        //if (ef.isDict()) {
        //    setDictDisplay(json, model, ef);
        //    return;
        //}
    }

    private static void setBillTypeDisplay(Map<String, Object> json, Object model, FieldObject ef) throws IllegalAccessException {
        /*if (ef.isTypeOnly() || model == null) {
            return;
        }

        String displayName = ef.getBillTypeAttr();
        if (StringUtils.isEmpty(displayName)) {
            displayName = ef.getName() + "Name";
        }
        Object value = ef.getValue(model);
        if (value == null) {
            json.put(displayName, null);
            return;
        }

        String billType = (String) value;

        // 默认按类名获取
        String displayValue = BillTypeUtils.getName(billType);
        if (displayValue == null) {
            // 兼容按编码获取
            displayValue = BillTypeUtils.getNameByNumber(billType);
        }
        if (StringUtils.isBlank(displayValue)) {
            // 兼容LC实体
            displayValue = LCModelUtil.getModelName(billType);
        }

        json.put(displayName, displayValue);*/
    }

    private static void setDateValue(Map<String, Object> json, Object value, FieldObject ef) {
        if (value == null) {
            return;
        }

        Date date = (Date) value;

        String format = "yyyy-MM-dd HH:mm:ss";
        if (ef.isDateFormat() && StringUtils.isNotBlank(ef.getDateFormat())) {
            format = ef.getDateFormat();
        }

        json.put(ef.getName(), DateUtil.parseDateToString(date, format));
    }

    private static void setDictDisplay(Map<String, Object> json, Object model, FieldObject ef) {
        /*try {
            String displayName = ef.getDictLabel();
            if (StringUtils.isEmpty(displayName)) {
                displayName = ef.getName() + "Label";
            }

            Object obj = ef.getValue(model);
            if (obj == null) {
                json.put(displayName, null);
                return;
            }

            String dictValue = (String) obj;

            String displayValue = DictUtils.getLabel(ef.getDictType(), dictValue);

            json.put(displayName, displayValue);
        } catch (Exception e) {
            log.error("获取【{}.{}】的关联数据字典Label错误，msg={}", model.getClass().getName(), ef.getName(), e.getMessage());
        }*/
    }

    private static void setEnumDisplay(Map<String, Object> json, Object model, FieldObject ef) {
        try {
            Object enumVal = ef.getValue(model);

            String displayName = ef.getEnumDesc();
            if (StringUtils.isEmpty(displayName)) {
                displayName = ef.getName() + "Desc";
            }

            if (enumVal == null) {
                json.put(displayName, null);
                return;
            }

            String displayValue = EnumUtils.getDesc(ef.getEnumType(), enumVal);
            json.put(displayName, displayValue);
        } catch (Exception e) {
            log.error("获取【{}.{}】的关联枚举Desc错误, msg={}", model.getClass().getName(), ef.getName(), e.getMessage());
        }
    }

    private static void setRefDisplay(Map<String, Object> json, Object model, FieldObject ef) {
        try {
            Object idObj = ef.getValue(model);
            if (idObj == null || !(idObj instanceof Long)) {
                return;
            }

            Long id = (Long) idObj;

            String keyPrefix = ef.getRefModelType() + "#" + id;

            String refName = ef.getName();
            if (refName.endsWith("Id")) {
                refName = StringUtils.removeLastWord(refName);
            }
            String attrs = ef.getRefAttrs();
            if (StringUtils.isNotEmpty(attrs)) {
                for (String attr : attrs.split(",")) {
                    String propName = attr.trim();
                    String displayName = null;
                    int index = attr.indexOf(":");
                    if (index > 0) {
                        propName = attr.substring(0, index);
                        displayName = attr.substring(index + 1);
                    } else {
                        displayName = refName + StringUtils.firstAlpha2UpperCase(attr);
                    }

                    String displayValue = null;
                    if (id > 0L) {
                        //String cacheKey = keyPrefix + "#" + propName;
                        displayValue = CacheUtils.getProperty(ef.getRefModelType(), propName, id);
                    }

                    json.put(displayName, displayValue);
                }
            } else {
                //String cacheKey = keyPrefix + "#name";
                String displayName = refName + "Name";

                String displayValue = null;
                if (id > 0L) {
                    displayValue = CacheUtils.getName(ef.getRefModelType(), id);
                }

                json.put(displayName, displayValue);
            }
        } catch (Exception e) {
            log.error("获取【{}.{}】的关联属性错误, msg={}", model.getClass().getName(), ef.getName(), e.getMessage());
        }
    }

    private static void setRefUnitDisplay(Map<String, Object> json, Object model, FieldObject ef, EntityObject eo) {
        /*try {
            String fieldName = ef.getName();
            if (fieldName.endsWith("Id")) {
                fieldName = StringUtils.removeLastWord(fieldName);
            }
            String typeField = ef.getName();
            FieldRefUnit fieldRefUnit = ef.getAnnotation(FieldRefUnit.class);
            if (fieldRefUnit.typeOnly()) {
                return;
            }
            if (StringUtils.isBlank(fieldRefUnit.type())) {
                typeField = fieldName + "Type";
            } else {
                typeField = fieldRefUnit.type();
            }
            FieldObject fieldObject = eo.getFieldObject(typeField);
            if (fieldObject == null) {
                return;
            }
            Object typeValue = fieldObject.getValue(model);
            if (!(typeValue instanceof Integer)) {
                return;
            }
            Object idVal = ef.getValue(model);
            if (fieldRefUnit.showName()) {
                // 显示往来单位名称
                String displayName = fieldName + "Name";
                if (!(idVal instanceof Long)) {
                    json.put(displayName, null);
                    return;
                }
                String displayValue = RefUnitUtils.getUnitName((Integer) typeValue, (Long) idVal);
                json.put(displayName, displayValue);
            }
            if (fieldRefUnit.showNumber()) {
                // 显示往来单位编码
                String displayName = fieldName + "Number";
                if (!(idVal instanceof Long)) {
                    json.put(displayName, null);
                    return;
                }
                String displayValue = RefUnitUtils.getUnitNumber((Integer) typeValue, (Long) idVal);
                json.put(displayName, displayValue);
            }
        } catch (Exception e) {
            log.error("处理往来单位显示【{}.{}】错误，msg={}", model.getClass().getName(), ef.getName(), e.getMessage());
        }*/
    }

    private static void setRefLCModelDisplay(Map<String, Object> json, Object model, FieldObject ef, EntityObject eo) {
        /*try {
            String fieldName = ef.getName();
            if (fieldName.endsWith("Id")) {
                fieldName = StringUtils.removeLastWord(fieldName);
            }
            FieldLCModel fieldLCModel = ef.getAnnotation(FieldLCModel.class);
            if (fieldLCModel.typeOnly()) {
                return;
            }
            String displayName = fieldLCModel.attr();
            if (StringUtils.isEmpty(displayName)) {
                displayName = fieldName + "Name";
            }
            Object value = ef.getValue(model);
            if (value == null) {
                json.put(displayName, null);
                return;
            }
            String modelType = (String) value;
            String displayValue = LCModelUtil.getModelName(modelType);
            json.put(displayName, displayValue);
        } catch (Exception e) {
            log.error("处理LCModel显示【{}.{}】错误，msg={}", model.getClass().getName(), ef.getName(), e.getMessage());
        }*/
    }

    private static JSONObject parseJson(Object object) {
        JSONObject jsonObject = JSONObject.parseObject(JSON.toJSONString(object,
                SerializerFeature.WriteMapNullValue,
                //                SerializerFeature.WriteNullNumberAsZero,
                //                SerializerFeature.WriteNullListAsEmpty,
                //                SerializerFeature.WriteNullStringAsEmpty,
                SerializerFeature.WriteNullBooleanAsFalse));
        return jsonObject;
    }

    private static void putValJson(JSONObject jsonObject, JsonObject json) {
        for (Map.Entry<String, Object> entry : jsonObject.entrySet()) {
            String key = entry.getKey();
            // if (!json.containsKey(key) || json.get(key) == null) {
            if (!json.containsKey(key)) {
                json.put(key, entry.getValue());
            }
        }
    }

    /**
     * 转换报表预警对象
     */
    private static Object convertRpt(Object object) throws Exception {
        // 转换报表预警对象
        if (object == null) {
            return null;
        }
        if (object.getClass().getSuperclass().getName().equals("com.cis.abel.scp.rpt.domain.RptBase")) {
            if (EntityUtils.isSimpleType(object.getClass())) {
                return object;
            }
            Set<Object> dataSet = new HashSet<>();

            return toJSON(object, dataSet);
        } else if (object instanceof List) {
            List list = (List) object;
            List<Object> array = new ArrayList<>();
            for (Object o : list) {
                o = convertRpt(o);
                array.add(o);
            }
            return array;
        }
        return object;
    }

}
